<template>
  <div class="flow-containers-chart main-box">
    <div class="controlsRow" v-if="props.showControls">
      <el-tooltip effect="dark" content="放大" placement="bottom">
        <el-button size="small" @click="zoomIn" >
          <el-icon><ZoomIn /></el-icon>
        </el-button>
      </el-tooltip>
      <el-tooltip effect="dark" content="缩小" placement="bottom">
        <el-button size="small" @click="zoomOut" >
          <el-icon><ZoomOut /></el-icon>
        </el-button>
      </el-tooltip>
      <el-tooltip effect="dark" content="自适应屏幕" placement="bottom">
        <el-button size="small" @click="fitViewport">
          <el-icon><FullScreen /></el-icon>
        </el-button>
      </el-tooltip>
    </div>
    <div class="canvas" ref="scFlowCanvas"></div>
  </div>
</template>

<script  setup>
import { CustomViewer as BpmnViewer } from '../utils/customBpmn'
import { defineProps, ref, onMounted, onUnmounted, defineEmits } from 'vue'
import { ZoomIn, ZoomOut, FullScreen } from '@element-plus/icons-vue'

const props = defineProps({
  detail: {
    type: Object,
    default: () => ({})
  },
  height: {
    type: String,
    default: '100%'
  },
  xml: {
    type: Object,
    default: () => ({
      'nodeData': [
        {
          "key": "Event_0y1tu11",
          "completed": true
        },
        {
          "key": "Flow_1f7n89n",
          "completed": true
        },
        {
          "key": "Flow_1q21jf",
          "completed": true
        },
        {
          "key": "Flow_1ymav5h",
          "completed": true
        },
        {
          "key": "Flow_12anmwq",
          "completed": false
        }
      ],
      'xmlData': "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<definitions xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:flowable=\"http://flowable.org/bpmn\" xmlns:bpmndi=\"http://www.omg.org/spec/BPMN/20100524/DI\" xmlns:omgdc=\"http://www.omg.org/spec/DD/20100524/DC\" xmlns:omgdi=\"http://www.omg.org/spec/DD/20100524/DI\" xmlns:bpmn2=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:dc=\"http://www.omg.org/spec/DD/20100524/DC\" xmlns:di=\"http://www.omg.org/spec/DD/20100524/DI\" typeLanguage=\"http://www.w3.org/2001/XMLSchema\" expressionLanguage=\"http://www.w3.org/1999/XPath\" targetNamespace=\"http://www.flowable.org/processdef\" id=\"diagram_Process_1666925398466\">\n  <process id=\"COA_LOT\" name=\"COA市场订单\" isExecutable=\"true\" xmlns:flowable=\"http://flowable.org/bpmn\" flowable:processCategory=\"COA_LOT\">\n    <documentation>{\n\t\"description\":\"COA市场订单申请审核\",\n\t\"formCustomCreatePath\":null,\n\t\"formCustomViewPath\":null,\n\t\"formId\":null,\n\t\"formType\":null\n}</documentation>\n    <startEvent id=\"Event_0y1tu11\" name=\"开始\" flowable:initiator=\"startUserId\"></startEvent>\n    <userTask id=\"Flow_1q21jf\" name=\"制单\" flowable:assignee=\"${startUserId}\" flowable:skipExpression=\"${skipNode}\"></userTask>\n    <sequenceFlow id=\"Flow_1ymav5h\" sourceRef=\"Event_0y1tu11\" targetRef=\"Flow_1q21jf\"></sequenceFlow>\n    <userTask id=\"Flow_12anmwq\" name=\"业务经理\" flowable:assignee=\"${deptLeader}\">\n      <extensionElements>\n        <flowable:property name=\"autoAgree\" value=\"autoAgree-2\"></flowable:property>\n      </extensionElements>\n      <multiInstanceLoopCharacteristics isSequential=\"false\" flowable:collection=\"userList_1743044124909\" flowable:elementVariable=\"assignee\">\n        <completionCondition>${ nrOfCompletedInstances &gt; 0 }</completionCondition>\n      </multiInstanceLoopCharacteristics>\n    </userTask>\n    <sequenceFlow id=\"Flow_1f7n89n\" sourceRef=\"Flow_1q21jf\" targetRef=\"Flow_12anmwq\"></sequenceFlow>\n    <userTask id=\"Activity_0pqkkkb\" name=\"分管领导\" flowable:assignee=\"${assignee}\">\n      <extensionElements>\n        <flowable:property name=\"autoAgree\" value=\"autoAgree-3\"></flowable:property>\n      </extensionElements>\n      <multiInstanceLoopCharacteristics isSequential=\"false\" flowable:collection=\"userList_1743044139879\" flowable:elementVariable=\"assignee\">\n        <completionCondition>${ nrOfCompletedInstances &gt; 0 }</completionCondition>\n      </multiInstanceLoopCharacteristics>\n    </userTask>\n    <sequenceFlow id=\"Flow_090qfki\" sourceRef=\"Flow_12anmwq\" targetRef=\"Activity_0pqkkkb\"></sequenceFlow>\n    <endEvent id=\"Event_1d62cx8\" name=\"结束\"></endEvent>\n    <sequenceFlow id=\"Flow_1paqyie\" sourceRef=\"Activity_0pqkkkb\" targetRef=\"Event_1d62cx8\"></sequenceFlow>\n  </process>\n  <bpmndi:BPMNDiagram id=\"BPMNDiagram_COA_LOT\">\n    <bpmndi:BPMNPlane bpmnElement=\"COA_LOT\" id=\"BPMNPlane_COA_LOT\">\n      <bpmndi:BPMNShape bpmnElement=\"Event_0y1tu11\" id=\"BPMNShape_Event_0y1tu11\">\n        <omgdc:Bounds height=\"36.0\" width=\"36.0\" x=\"222.0\" y=\"162.0\"></omgdc:Bounds>\n      </bpmndi:BPMNShape>\n      <bpmndi:BPMNShape bpmnElement=\"Flow_1q21jf\" id=\"BPMNShape_Flow_1q21jf\">\n        <omgdc:Bounds height=\"80.0\" width=\"100.0\" x=\"310.0\" y=\"140.0\"></omgdc:Bounds>\n      </bpmndi:BPMNShape>\n      <bpmndi:BPMNShape bpmnElement=\"Flow_12anmwq\" id=\"BPMNShape_Flow_12anmwq\">\n        <omgdc:Bounds height=\"80.0\" width=\"100.0\" x=\"470.0\" y=\"140.0\"></omgdc:Bounds>\n      </bpmndi:BPMNShape>\n      <bpmndi:BPMNShape bpmnElement=\"Activity_0pqkkkb\" id=\"BPMNShape_Activity_0pqkkkb\">\n        <omgdc:Bounds height=\"80.0\" width=\"100.0\" x=\"630.0\" y=\"140.0\"></omgdc:Bounds>\n      </bpmndi:BPMNShape>\n      <bpmndi:BPMNShape bpmnElement=\"Event_1d62cx8\" id=\"BPMNShape_Event_1d62cx8\">\n        <omgdc:Bounds height=\"36.0\" width=\"36.0\" x=\"792.0\" y=\"162.0\"></omgdc:Bounds>\n      </bpmndi:BPMNShape>\n      <bpmndi:BPMNEdge bpmnElement=\"Flow_1ymav5h\" id=\"BPMNEdge_Flow_1ymav5h\">\n        <omgdi:waypoint x=\"258.0\" y=\"180.0\"></omgdi:waypoint>\n        <omgdi:waypoint x=\"310.0\" y=\"180.0\"></omgdi:waypoint>\n      </bpmndi:BPMNEdge>\n      <bpmndi:BPMNEdge bpmnElement=\"Flow_1f7n89n\" id=\"BPMNEdge_Flow_1f7n89n\">\n        <omgdi:waypoint x=\"410.0\" y=\"180.0\"></omgdi:waypoint>\n        <omgdi:waypoint x=\"470.0\" y=\"180.0\"></omgdi:waypoint>\n      </bpmndi:BPMNEdge>\n      <bpmndi:BPMNEdge bpmnElement=\"Flow_090qfki\" id=\"BPMNEdge_Flow_090qfki\">\n        <omgdi:waypoint x=\"570.0\" y=\"180.0\"></omgdi:waypoint>\n        <omgdi:waypoint x=\"630.0\" y=\"180.0\"></omgdi:waypoint>\n      </bpmndi:BPMNEdge>\n      <bpmndi:BPMNEdge bpmnElement=\"Flow_1paqyie\" id=\"BPMNEdge_Flow_1paqyie\">\n        <omgdi:waypoint x=\"730.0\" y=\"180.0\"></omgdi:waypoint>\n        <omgdi:waypoint x=\"792.0\" y=\"180.0\"></omgdi:waypoint>\n      </bpmndi:BPMNEdge>\n    </bpmndi:BPMNPlane>\n  </bpmndi:BPMNDiagram>\n</definitions>"
    })
  },
  showControls: {
    type: Boolean,
    default: true
  },
  showTooltips: {
    type: Boolean,
    default: true
  },
  theme: {
    type: Object,
    default: () => ({
      completedColor: 'green',
      pendingColor: 'orange'
    })
  },
  tooltipFormatter: {
    type: Function,
    default: null
  }
})

const emit = defineEmits(['node-click'])

let bpmnModeler
const scFlowCanvas = ref(null)
const zoomLevel = ref(1)

// 窗口大小监听
let resizeTimer = null
const handleResize = () => {
  clearTimeout(resizeTimer)
  resizeTimer = setTimeout(() => {
    if (bpmnModeler) bpmnModeler.get('canvas').zoom('fit-viewport')
  }, 300)
}

const initFlow = async () => {
  try {
    bpmnModeler && bpmnModeler.destroy()
    bpmnModeler = new BpmnViewer({
      container: scFlowCanvas.value,
      height: props.height
    })
    await loadFlowCanvas(props.xml)
  } catch (err) {
    console.error('流程初始化失败:', err)
  }
}

const loadFlowCanvas = async (flowData) => {
  try {
    await bpmnModeler.importXML(flowData.xmlData)
    fitViewport()
    if (flowData.nodeData.length > 0) {
      fillColor(flowData.nodeData)
      setupEvents()
    }
  } catch (err) {
    console.error('XML解析失败:', err.message)
  }
}

const setupEvents = () => {
  const eventBus = bpmnModeler.get('eventBus')

  // 节点点击事件
  eventBus.on('element.click', (e) => {
    emit('node-click', e.element)
  })

  if (props.showTooltips) {
    const overlays = bpmnModeler.get('overlays')
    eventBus.on('element.hover', (e) => {
      if (e.element.type === 'bpmn:UserTask') {
        showTooltip(e, overlays)
      }
    })
    eventBus.on('element.out', () => overlays.clear())
  }
}

const showTooltip = (e, overlays) => {
  const content = props.tooltipFormatter
    ? props.tooltipFormatter(e.element, props.detail)
    : defaultTooltipFormatter(e.element, props.detail)

  if (!content) return

  const tempDiv = document.createElement('div')
  tempDiv.innerHTML = content
  tempDiv.className = 'tipBox'

  overlays.add(e.element.id, 'note', {
    position: { top: e.element.height + 10, left: 0 },
    html: tempDiv
  })
}

const defaultTooltipFormatter = (element, detail) => {
  const task = detail.taskList?.find(t => t.taskDefKey === element.id) || {}
  return `
    <div class="task-title">${element.businessObject.name}</div>
    <p>审批人：${task.assigneeName || '-'}</p>
    <p>状态：${task.completed ? '已完成' : '进行中'}</p>
    ${task.comments ? `<p>审批意见：${task.comments}</p>` : ''}
  `
}

const fitViewport = () => {
  zoomLevel.value = bpmnModeler.get('canvas').zoom('fit-viewport', 'auto')
}

const zoomIn = () => {
  zoomLevel.value = Math.min(2, zoomLevel.value + 0.1)
  bpmnModeler.get('canvas').zoom(zoomLevel.value)
}

const zoomOut = () => {
  zoomLevel.value = Math.max(0.2, zoomLevel.value - 0.1)
  bpmnModeler.get('canvas').zoom(zoomLevel.value)
}


const fillColor = (nodeData) => {
  const canvas = bpmnModeler.get('canvas');
  const { completedColor, pendingColor } = props.theme;

  document.documentElement.style.setProperty('--completed-color', completedColor);
  document.documentElement.style.setProperty('--pending-color', pendingColor);

  const allShapes = bpmnModeler.get('elementRegistry').getAll();
  const todoTask = nodeData.find((m) => !m.completed);

  allShapes.forEach((element) => {
    const shapeId = element.businessObject.id;
    const shapeType = element.businessObject.$type;
    const node = nodeData.find((m) => m.key === shapeId);

    if (!node) return;

    const marker = node.completed ? 'highlight' : 'highlight-todo';
    canvas.addMarker(shapeId, marker);

    // 处理不同类型节点的连线
    if (['bpmn:UserTask', 'bpmn:ExclusiveGateway', 'bpmn:ParallelGateway', 'bpmn:StartEvent'].includes(shapeType)) {
      element.outgoing?.forEach((flow) => {
        const targetNode = nodeData.find((m) => m.key === flow.target.id);
        if (targetNode) {
          const flowMarker = targetNode.completed ? 'highlight' : 'highlight-todo';
          canvas.addMarker(flow.id, flowMarker);

          // 特殊处理当前待办任务的连线
          if (todoTask && node.key === todoTask.key && !todoTask.completed) {
            canvas.addMarker(flow.target.id, 'highlight-todo');
          }
        }
      });
    }

    // 特殊处理结束事件
    if (shapeType === 'bpmn:EndEvent') {
      const endTask = nodeData[nodeData.length - 1];
      if (endTask.key === shapeId && endTask.completed) {
        canvas.addMarker(shapeId, 'highlight');
      }
    }
  });
};



onMounted(() => {
  initFlow()
  window.addEventListener('resize', handleResize)
})

onUnmounted(() => {
  bpmnModeler && bpmnModeler.destroy()
  window.removeEventListener('resize', handleResize)
})

defineExpose({
  initFlow
})
</script>

<style scoped lang="scss">

:root {
  --completed-color: green;
  --pending-color: orange;
}
::v-deep(.djs-container){
  background-image: linear-gradient(90deg, hsl(0deg 0% 78.4% / 15%) 6%, transparent 0),
    linear-gradient(hsl(0deg 0% 78.4% / 15%) 6%, transparent 0);
  background-size: 30px 30px;
  min-height: 200px;
}

::v-deep(.bjs-powered-by) {
  display: none;
}
::v-deep(.bjs-breadcrumbs) {
  display: none;

}

.flow-containers-chart {
  position: relative;
  width: 100%;
  height: 100%;

  .canvas {
    width: 100%;
    height: calc(100% - 40px);
    padding: 20px;
    box-sizing: border-box;
  }

  ::v-deep(.highlight.djs-shape .djs-visual> :nth-child(1)) {
    fill: var(--completed-color) !important;
    fill-opacity: 0.2 !important;
    stroke: var(--completed-color) !important;
  }

  ::v-deep(.highlight-todo.djs-shape .djs-visual> :nth-child(1)) {
    fill: var(--pending-color) !important;
    fill-opacity: 0.2 !important;
    stroke: var(--pending-color) !important;
    stroke-dasharray: 4px !important;
  }

  ::v-deep(.highlight.djs-connection>.djs-visual>path) {
    stroke: var(--completed-color) !important;
  }

  ::v-deep(.highlight-todo.djs-connection>.djs-visual>path) {
    stroke: var(--pending-color) !important;
    stroke-dasharray: 4px !important;
  }
}

::v-deep(.tipBox) {
  padding: 12px;
  width: 200px;
  font-size: 12px;
  color: #fff;
  background: rgba(34, 34, 34, 0.9);
  border-radius: 4px;
  line-height: 1.5;

  .task-title {
    font-weight: bold;
    color: #409eff;
    margin-bottom: 8px;
  }

  p {
    margin: 4px 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}
.controlsRow {
  display: flex;
  align-items: center;
  padding: 10px;
  background-color: #f0f0f0;
}
</style>